Skip to content

fix(deps): align @cipherstash/auth with its platform bindings (rc.2 B1)#679

Merged
coderdan merged 1 commit into
mainfrom
fix/auth-binding-lockstep
Jul 17, 2026
Merged

fix(deps): align @cipherstash/auth with its platform bindings (rc.2 B1)#679
coderdan merged 1 commit into
mainfrom
fix/auth-binding-lockstep

Conversation

@coderdan

@coderdan coderdan commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the rc.2 GA blocker B1: every project-local npm install of the CLI or SDK dies at startup with Failed to load native binding for <platform> (reproduced on 5/5 surfaces in the 2026-07-17 skilltester run).

Root cause

@cipherstash/auth pins its six platform bindings as exact-version optional peerDependencies. Our catalog compensates by declaring the bindings in stack/stash/wizard's own optionalDependencies — but Dependabot bumped those six entries to 0.42.0 (4164f18) while @cipherstash/auth itself, individually ignored in dependabot.yml, stayed 0.41.0. With the set skewed, npm nests per-consumer binding copies (node_modules/stash/node_modules/@cipherstash/auth-darwin-arm64) that the hoisted auth package can never resolve.

Reproduced against the published rc.2 with the skilltester's exact sequence, and verified fixed with aligned versions (npm dedupes to one hoisted, resolvable binding):

npm i @cipherstash/stack@1.0.0-rc.2 @cipherstash/stack-supabase@1.0.0-rc.2
npm i -D stash@1.0.0-rc.2
node -e "require('@cipherstash/auth')"   # fatal before, loads after

Changes

  1. Catalog: all seven @cipherstash/auth* entries move to 0.42.0, with the lockstep invariant documented as load-bearing (not tidiness).
  2. Dependabot: @cipherstash/auth-* joins @cipherstash/auth on the ignore list — a partial bump of this set is never safe.
  3. Supply-chain e2e: new test asserting the seven catalog entries share one version. Verified red against the exact rc.2 skew, green after the fix — this is the guard pnpm-workspace.yaml's "keep in lockstep" comment always needed.

Changeset: patch for @cipherstash/stack, stash, @cipherstash/wizard.

Verification

  • auth 0.42.0 runtime compatibility: stack 828/828 (includes the live ZeroKMS suites, which authenticate through auth), cli 513/513, wizard 145/145. (searchable-json-pg.test.ts needs DATABASE_URL and didn't run locally — unrelated to auth; covered in CI.)
  • Supply-chain e2e: 18/18, including the new lockstep guard.

Upstream follow-up (cipherstash-suite)

The durable fix is for @cipherstash/auth to declare its bindings as its own optionalDependencies (the napi-rs default), so consumers never pin them at all — its sibling @cipherstash/profile in the same repo already does exactly this, as does @cipherstash/protect-ffi. Filing that separately; once auth ships it, the catalog binding entries here can be deleted entirely.

https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w

Summary by CodeRabbit

  • Bug Fixes

    • Fixed project-local CLI and SDK startup failures caused by mismatched authentication package versions.
    • Ensured the authentication package and platform bindings use a consistent version.
  • Chores

    • Updated related package versions and dependency configuration.
    • Added validation to prevent future version mismatches.

The rc.2 skilltester run found every project-local npm install of the
CLI/SDK dying at startup with "Failed to load native binding" (B1,
reproduced 5/5 surfaces). Root cause: Dependabot bumped the six
@cipherstash/auth-* catalog entries to 0.42.0 while @cipherstash/auth —
individually ignored in dependabot.yml — stayed 0.41.0. auth pins its
bindings as exact-version optional peerDependencies, so the skew makes
npm nest per-consumer binding copies that the hoisted auth package
cannot resolve.

Reproduced against the published rc.2 and verified fixed with aligned
versions (npm dedupes to one hoisted binding; auth loads):

  npm i @cipherstash/stack@1.0.0-rc.2 @cipherstash/stack-supabase@1.0.0-rc.2
  npm i -D stash@1.0.0-rc.2
  node -e "require('@cipherstash/auth')"   # fatal before, loads after

Three changes so it can't recur:
- catalog: all seven @cipherstash/auth* entries at 0.42.0, with the
  lockstep invariant documented as load-bearing
- dependabot: ignore @cipherstash/auth-* alongside @cipherstash/auth —
  partial bumps of this set are never safe
- supply-chain e2e: a test that fails on any version skew across the
  seven entries (verified red against the exact rc.2 skew)

auth 0.42.0 compatibility verified: stack 828/828 (incl. live ZeroKMS
auth), cli 513/513, wizard 145/145.

Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
@coderdan
coderdan requested a review from a team as a code owner July 17, 2026 01:12
@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: c69e6b5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@cipherstash/stack Patch
stash Patch
@cipherstash/wizard Patch
@cipherstash/bench Patch
@cipherstash/prisma-next Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-supabase Patch
@cipherstash/test-kit Patch
@cipherstash/basic-example Patch
@cipherstash/prisma-next-example Patch
@cipherstash/e2e Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d72a4c0-0a50-44d9-8c25-22f8c14f8c80

📥 Commits

Reviewing files that changed from the base of the PR and between 3a86939 and c69e6b5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • .changeset/auth-binding-lockstep.md
  • .github/dependabot.yml
  • e2e/tests/supply-chain.e2e.test.ts
  • pnpm-workspace.yaml

📝 Walkthrough

Walkthrough

Updates the auth workspace catalog to version 0.42.0, documents lockstep binding requirements, adds an end-to-end catalog consistency check, and records patch releases for related packages.

Changes

Auth binding lockstep

Layer / File(s) Summary
Lockstep version policy
.github/dependabot.yml, pnpm-workspace.yaml
Pins @cipherstash/auth to 0.42.0 and documents exact-version coordination with its six platform bindings.
Lockstep validation and release metadata
e2e/tests/supply-chain.e2e.test.ts, .changeset/auth-binding-lockstep.md
Validates that all seven auth catalog entries share one version and records patch releases for related packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: calvinbrewer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: aligning @cipherstash/auth with its platform bindings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/auth-binding-lockstep

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@freshtonic freshtonic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Tightly-scoped fix for the rc.2 GA blocker (B1): @cipherstash/auth was catalog-pinned at 0.41.0 while its six @cipherstash/auth-* platform bindings sat at 0.42.0. Because auth declares those bindings as exact-version optional peer dependencies, the skew made npm nest per-consumer binding copies the hoisted auth package couldn't resolve — so every project-local CLI/SDK install died at startup with Failed to load native binding. The fix bumps auth to 0.42.0 (all seven now in lockstep), bars Dependabot from bumping any of them independently, and adds a CI guard so the skew can't recur.

The root-cause writeup and the reproduction are excellent, and the fix matches the diagnosis exactly.

What's right

  • The core change is correct and minimal. pnpm-workspace.yaml catalog @cipherstash/auth: 0.41.0 → 0.42.0; pnpm-lock.yaml re-resolves cleanly to 0.42.0(...bindings@0.42.0) across all three consumers (stack, cli, wizard). Verified all 7 catalog entries now read 0.42.0.
  • The Dependabot guard is the right shape. @cipherstash/auth-* correctly wildcard-matches the six bindings without over-matching @cipherstash/auth (listed separately) or unrelated scopes. A partial bump of this set is genuinely never safe, so ignoring the whole family and bumping manually is correct.
  • The e2e guard is well-designed. Asserting both authEntries.length === 7 and versions.size === 1 means a future binding added/removed upstream trips the count assertion (forcing a deliberate update), while any version skew trips the set assertion — with a message that prints the offending name@version pairs. It reads the catalog (pnpm-workspace.yaml), which is the single source of truth since every consumer uses catalog:repo. This is the guard the "keep in lockstep" comment always implied but never enforced.
  • Docs/process hygiene: load-bearing rationale is now captured in three places (workspace comment, dependabot comment, test comment) rather than the old "for tidiness" framing; changeset present (patch for stack/stash/wizard); the durable upstream fix (auth declaring its bindings as its own optionalDependencies, like the sibling @cipherstash/profile and @cipherstash/protect-ffi) is correctly identified as follow-up rather than attempted here.

Notes — minor, non-blocking

1. Unrelated lockfile churn — please confirm intentional. Beyond the auth lines, the pnpm-lock.yaml diff shifts a @vitest/mocker@3.2.7 peer resolution from @types/node@22.20.1 to @types/node@26.1.1 (and adds a matching snapshot block). It's benign — @types/node@26.1.1 already resolves 21× in the base lockfile, and this only touches a dev/test-only mocker peer, not any runtime dependency — but it's incidental to an auth-alignment PR and unexplained in the description. Worth a sentence confirming it's just re-resolution drift from regenerating the lock, so a reviewer doesn't have to reverse-engineer it.

2. (Optional) The guard covers the catalog, not the lockfile or consumer manifests. That's the correct primary check — the catalog is the source of truth and frozen-lockfile CI keeps the lock honest. No change needed; noting only so the guard's scope is explicit: it would not catch a hand-edited consumer optionalDependencies that bypassed catalog:repo, but nothing in-repo does that today.

Verdict

Correct, well-tested, and the regression guard closes the exact hole that produced the blocker — this is good to merge. The only ask is a one-line confirmation that the @types/node lockfile shuffle (item 1) is intentional re-resolution and not a stray change.

@auxesis auxesis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, thanks for this @coderdan.

@freshtonic freshtonic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the fix is correct, minimal, and well-guarded, and it resolves the rc.2 GA blocker (B1). The catalog lockstep bump, the Dependabot @cipherstash/auth-* ignore, and the new supply-chain e2e guard together close exactly the hole that caused it. My earlier review has the detail; the only open item is the minor, non-blocking ask to confirm the unrelated @types/node lockfile re-resolution is intentional.

@coderdan

Copy link
Copy Markdown
Contributor Author

@freshtonic — confirmed intentional, and on inspection it's slightly better than re-resolution drift: it's pnpm repairing a stale cross-wiring in the existing lock.

On main, the two vitest@3.2.7 snapshot flavours look like this:

vitest@3.2.7(@types/node@22.20.1)…:
  '@vitest/mocker': 3.2.7(vite@7.3.6(@types/node@22.20.1)…)   ← self-consistent
vitest@3.2.7(@types/node@26.1.1)…:
  '@vitest/mocker': 3.2.7(vite@7.3.6(@types/node@22.20.1)…)   ← cross-wired to the other flavour

i.e. the 26.1.1-flavoured vitest was borrowing the mocker keyed on the 22.20.1-flavoured vite — a leftover from whichever earlier partial re-resolution introduced the second flavour. Regenerating the lock for the auth bump made the 26.1.1 peer graph self-consistent (hence the new @vitest/mocker@3.2.7(vite@7.3.6(@types/node@26.1.1)…) snapshot block) and left the 22.20.1 flavour untouched.

Dev/test-only (@vitest/mockervite peer), no runtime dependency involved, and the frozen-lockfile CI matrix (Node 22/24, Bun, Deno) is green on exactly this lock. Agreed on your note 2 as well — the catalog is the deliberate scope of the guard, with frozen-lockfile CI keeping the lock honest downstream of it.

https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w

@coderdan
coderdan merged commit 8b2551a into main Jul 17, 2026
10 checks passed
@coderdan
coderdan deleted the fix/auth-binding-lockstep branch July 17, 2026 01:23
@coderdan

Copy link
Copy Markdown
Contributor Author

Tracked by #718.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants